package Test;


import Navi_utils.GeoHash;
import encryption.Hash;
import info.debatty.java.lsh.LSHMinHash;
import objects.LatLonPoint;
import objects.LocationBean;
import org.ujmp.core.DenseMatrix;
import org.ujmp.core.Matrix;

import java.security.SecureRandom;
import java.util.*;

import static parameter.parameter_length.vector_length;
import static utils.matrix.mathDeterminantCalculationValue;

public class Fuzzy_test {
    public static Matrix V;
    public static Matrix SK1;
    public static Matrix SK2;
    public static Matrix HKP;
    public static Matrix I22;
    public static Matrix Q11;
    public static Matrix Q22;
    public static LSHMinHash lshMinHash = new LSHMinHash(100, 100, 36*36);


    // 生成模糊搜索密钥SK(SK1,SK2,V)
    public static Matrix generateSearchKey(int k1, int k2) {
        Random random = new Random();
        // 生成SK1和SK2
        SK1 = DenseMatrix.Factory.zeros(k1, k2);
        SK2 = DenseMatrix.Factory.zeros(k1, k2);
        for (int i = 0; i < k1; i++) {
            for (int j = 0; j < k2; j++) {
                SK1.setAsBoolean(random.nextBoolean(), i, j);
                SK2.setAsBoolean(random.nextBoolean(), i, j);
            }
        }
        V = DenseMatrix.Factory.rand(1, k2);
        for (int i = 0; i < k2; i++) {
            V.setAsBoolean(random.nextBoolean(), 0, i);
        }
        HKP = DenseMatrix.Factory.zeros(100, 100);
        for (int i = 0; i < 100; i++) {
            for (int j = 0; j < 100; j++) {
                HKP.setAsBoolean(random.nextBoolean(), i, j);
            }
        }
        return null;
    }


    //对输入数据建立一个向量并求索引 选用100个桶的LSH
    public static double[] BuildIndex(String temp, byte[] key, LSHMinHash lshMinHash) {
        //初始化一个长度为26*26的向量vector
        //初始为全false(0)
        double[] vector = new double[vector_length];
        //对输入进行二元分词并将对应向量位置置为1
        for (int i = 0; i < temp.length() - 1; i++) {
            //将字母转化为ASCII
            //求出要在vector中标记为1的位置
            //第一个字母所在位置*26+第二个字母所在位置,eg aa=0 bb=27 cc = 26*2+2 zz = 25*26+25
            int temp1 = 0;
            if (Character.isDigit(temp.charAt(i))) {
                temp1 = ((int) temp.charAt(i) - 48) * 36;
            } else {
                temp1 = ((int) temp.charAt(i) - 97 + 10) * 36;
            }
            int temp2 = 0;
            if (Character.isDigit(temp.charAt(i + 1))) {
                temp2 = ((int) temp.charAt(i + 1) - 48);
            } else {
                temp2 = ((int) temp.charAt(i + 1) - 97 + 10);
            }
            vector[temp1 + temp2] = 1;
        }
        double[] Index = vector;
        return Index;
    }

    public static double[] BuildIndex(LocationBean locationBean, byte[] key, LSHMinHash lshMinHash) {
        GeoHash geoHash = new GeoHash(locationBean);
        String temp = geoHash.getGeoHashBase32();
//初始化一个长度为26*26的向量vector
        //初始为全false(0)
        double[] vector = new double[vector_length];
        //对输入进行二元分词并将对应向量位置置为1
        for (int i = 0; i < temp.length() - 1; i++) {
            //将字母转化为ASCII
            //求出要在vector中标记为1的位置
            //第一个字母所在位置*26+第二个字母所在位置,eg aa=0 bb=27 cc = 26*2+2 zz = 25*26+25
            int temp1 = 0;
            if (Character.isDigit(temp.charAt(i))) {
                temp1 = ((int) temp.charAt(i) - 48) * 36;
            } else {
                temp1 = ((int) temp.charAt(i) - 97 + 10) * 36;
            }
            int temp2 = 0;
            if (Character.isDigit(temp.charAt(i + 1))) {
                temp2 = ((int) temp.charAt(i + 1) - 48);
            } else {
                temp2 = ((int) temp.charAt(i + 1) - 97 + 10);
            }
            vector[temp1 + temp2] = 1;
        }
        double[] Index = vector;
        return Index;
    }

    //通过S将I分割成I`和I``，然后用SK1和SK2加密
    //暂定的分割方式
    public static Map<String, Matrix> Enc_Index(Matrix SK1, Matrix SK2, Matrix V, double[] I) {
        double[] I1 = new double[I.length];
        double[] I2 = new double[I.length];
        Random random = new Random();
        for (int i = 0; i < V.getColumnCount(); i++) {
            if (V.getAsBoolean(0, i)) {
                //I`[t] = I``[t] =I[t]
                I1[i] = I2[i] = I[i];

            } else {
                //生成100以内的随机数
                int temp = random.nextInt(100);
                // 这里直接整数处以2等整数了，存在0.5情况导致信息丢失
                I1[i] = (double) I[i] / 2 + temp;
                I2[i] = (double) I[i] / 2 - temp;
            }
        }
        //求加密后的安全索引 EncIndex(I) = {SK1^T·I1,SK2^T·I2}
//        Matrix SK1_I1 = DenseMatrix.Factory.importFromArray(I1).mtimes(SK1.transpose());
//        Matrix SK2_I2 = DenseMatrix.Factory.importFromArray(I2).mtimes(SK2.transpose());
        Matrix SK1_I1 = SK1.transpose().mtimes(DenseMatrix.Factory.importFromArray(I1).transpose());
        Matrix SK2_I2 = SK2.transpose().mtimes(DenseMatrix.Factory.importFromArray(I2).transpose());
        // 将索引加密结果保存到EncIndex
        Map<String, Matrix> EncIndex = new HashMap<>();
        EncIndex.put("SK1_I1", SK1_I1);
        EncIndex.put("SK2_I2", SK2_I2);
        return EncIndex;
    }

    // 对查询向量进行加密
    public static Map<String, Matrix> Enc_Query(Matrix SK1, Matrix SK2, Matrix V, double[] Q) {
        double[] Q1 = new double[Q.length];
        double[] Q2 = new double[Q.length];
        Random random = new Random();
        for (int i = 0; i < V.getColumnCount(); i++) {
            if (V.getAsBoolean(0, i)) {
                //生成100以内的随机数
                int temp = random.nextInt(100);
                // 这里直接整数处以2等整数了，存在0.5情况导致信息丢失
                Q1[i] = (double) Q[i] / 2 + temp;
                Q2[i] = (double) Q[i] / 2 - temp;
            } else {
                Q1[i] = Q2[i] = Q[i];
            }
        }
        Q11 = DenseMatrix.Factory.importFromArray(Q1);
        Q22 = DenseMatrix.Factory.importFromArray(Q2);
        //求加密后的安全索引 EncQuery(I) = {SK1^-1·I1,SK2^-1·I2}
//        Matrix SK1_Q1 = DenseMatrix.Factory.importFromArray(Q1).mtimes(SK1.inv());
//        Matrix SK2_Q2 = DenseMatrix.Factory.importFromArray(Q2).mtimes(SK2.inv());
        Matrix SK1_Q1 = SK1.inv().mtimes(DenseMatrix.Factory.importFromArray(Q1).transpose());
        Matrix SK2_Q2 = SK2.inv().mtimes(DenseMatrix.Factory.importFromArray(Q2).transpose());
        // 将索引加密结果保存到EncIndex
        Map<String, Matrix> EncQuery = new HashMap<>();
        EncQuery.put("SK1_Q1", SK1_Q1);
        EncQuery.put("SK2_Q2", SK2_Q2);
        return EncQuery;
    }

    // 对查询语句生成陷门
    public static Map<String, Matrix> trapDoor(Matrix SK1, Matrix SK2, Matrix HKP, Matrix V, LocationBean locationBean, LSHMinHash lshMinHash) {
        byte[] key = new byte[(int) HKP.getColumnCount()];
        for (int i = 0; i < key.length; i++) {
            key[i] = HKP.getAsByte(0, i);
        }

        double[] vector = BuildIndex(locationBean, key, lshMinHash);
        Map<String, Matrix> EncSK;
        EncSK = Enc_Query(SK1, SK2, V, vector);
        return EncSK;
    }

    // 对查询语句生成陷门
    public static Map<String, Matrix> trapDoor(Matrix SK1, Matrix SK2, Matrix HKP, Matrix V, String message, LSHMinHash lshMinHash) {
        byte[] key = new byte[(int) HKP.getColumnCount()];
        for (int i = 0; i < key.length; i++) {
            key[i] = HKP.getAsByte(0, i);
        }

        double[] vector = BuildIndex(message, key, lshMinHash);
        Map<String, Matrix> EncSK;
        EncSK = Enc_Query(SK1, SK2, V, vector);
        return EncSK;
    }

    //进行查询，计算安全索引向量Encsk(I)和陷门函数EncSK(Q)之间的内积
    public static void Search(Map<String, Matrix> EncIndex, Map<String, Matrix> EncSK) {
        Matrix eee = (EncIndex.get("SK1_I1").transpose()).mtimes(EncSK.get("SK1_Q1")).plus((EncIndex.get("SK2_I2").transpose()).mtimes(EncSK.get("SK2_Q2")));
        System.out.println("求得内积结果：" + eee.getAsDouble(0, 0));
    }

    public static void main(String[] args) {
        String message = "baojishijintaiqu";
        String message2 = "baojishijintaqu";
        String message3 = "baojishiintiqu";
        String message4 = "beijinshihaidingqu";
        String message5 = "baoijshijintaiqu";

        int k1, k2;
        k1 = k2 = 36*36;
        generateSearchKey(k1, k2);
        byte[] key = new byte[(int) HKP.getColumnCount()];
        System.out.println(key.length);
        System.out.println(HKP.getColumnCount());
        //获取加密索引要用的密钥
        for (int i = 0; i < key.length; i++) {
            key[i] = HKP.getAsByte(0, i);
        }
        LocationBean locationBean = new LocationBean(45.9999999, 45.9999999);
        LocationBean locationBean1 = new LocationBean(45.999999, 45.9999987);
        LocationBean locationBean2 = new LocationBean(47.7777777, 47.7777777);
        LocationBean locationBean3 = new LocationBean(48.6666666, 48.6666666);
        LocationBean locationBean4 = new LocationBean(49.5555555, 49.5555555);
        double[] I = BuildIndex(locationBean, key, lshMinHash);
        //加密后计算出来的内积  内积越高，相似度越高
        Search(Enc_Index(SK1, SK2, V, I), trapDoor(SK1, SK2, HKP, V,locationBean1, lshMinHash));
        Search(Enc_Index(SK1, SK2, V, I), trapDoor(SK1, SK2, HKP, V,locationBean2, lshMinHash));
        Search(Enc_Index(SK1, SK2, V, I), trapDoor(SK1, SK2, HKP, V,locationBean3, lshMinHash));
        Search(Enc_Index(SK1, SK2, V, I), trapDoor(SK1, SK2, HKP, V,locationBean4, lshMinHash));
    }
}
